Skip to content

Add GitHub API rate limit handling UI#533

Merged
mehul-m-prajapati merged 2 commits into
GitMetricsLab:mainfrom
ananyadarna:feature/github-api-rate-limit-ui
May 29, 2026
Merged

Add GitHub API rate limit handling UI#533
mehul-m-prajapati merged 2 commits into
GitMetricsLab:mainfrom
ananyadarna:feature/github-api-rate-limit-ui

Conversation

@ananyadarna
Copy link
Copy Markdown
Contributor

@ananyadarna ananyadarna commented May 26, 2026

Related Issue


Description

Implemented GitHub API rate limit handling in the Activity Feed component.

Changes Made

  • Added handling for GitHub API rate limit errors (403)
  • Displayed reset time using X-RateLimit-Reset
  • Displayed remaining request count using X-RateLimit-Remaining
  • Added proper error state handling
  • Improved loading and empty state handling
  • Fixed dark mode text visibility issues
  • Added responsive error alert UI for both light and dark themes

How Has This Been Tested?

  • Tested API success response
  • Tested GitHub API rate limit exceeded response
  • Verified reset time rendering
  • Verified remaining request count rendering
  • Verified loading state functionality
  • Verified empty state rendering
  • Tested in both light mode and dark mode
  • Tested responsive UI behavior

Screenshots (if applicable)

  • Added screenshots showing:
    • Rate limit exceeded alert
    • Dark mode UI
    • Light mode UI
    • Activity Feed rendering

Type of Change

  • Bug fix
  • New feature
  • Code style update
  • Breaking change
  • Documentation update

Summary by CodeRabbit

  • Bug Fixes

    • Added error handling for GitHub API rate limit responses with reset time display.
    • Improved handling of failed API requests with user-friendly error messages.
  • UI/Style

    • Added error banner to display API errors.
    • Enhanced dark mode support for page headings and activity feed text.

Review Change Stack

@netlify
Copy link
Copy Markdown

netlify Bot commented May 26, 2026

Deploy Preview for github-spy ready!

Name Link
🔨 Latest commit df89ade
🔍 Latest deploy log https://app.netlify.com/projects/github-spy/deploys/6a19c82656eec2000925e2c1
😎 Deploy Preview https://deploy-preview-533--github-spy.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Warning

Review limit reached

@mehul-m-prajapati, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 23 minutes and 11 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 122a1634-9438-49c7-a28b-c928f12d1196

📥 Commits

Reviewing files that changed from the base of the PR and between c877d31 and df89ade.

📒 Files selected for processing (1)
  • src/components/ActivityFeed.tsx
📝 Walkthrough

Walkthrough

ActivityFeed now detects GitHub API rate limit responses (HTTP 403), reads the X-RateLimit-Reset header, and displays a user-facing error message with the reset time. Generic fetch errors are caught and shown with a fallback message. Dark-mode text color classes were added to headings and empty states in both the ActivityFeed and Activity page components.

Changes

GitHub API Rate Limit Detection and Error Display

Layer / File(s) Summary
Error state and GitHub API rate limit detection
src/components/ActivityFeed.tsx
Added error React state; updated fetch flow inside useEffect to detect HTTP 403 responses by reading X-RateLimit-Remaining and X-RateLimit-Reset headers, construct a detailed rate-limit error message, clear events, and throw for non-OK responses. Catch block sets a generic error message; finally reliably clears loading state.
Error banner UI and dark-mode text styling
src/components/ActivityFeed.tsx, src/pages/Activity.tsx
Updated render to conditionally show an error banner when error is set; applied dark-mode styling classes (text-black and dark:text-white) to the Activity Feed heading in ActivityFeed and the <h1> heading in Activity page; set "No activity found" empty-state text to use dark-mode text classes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • GitMetricsLab/github_tracker#276: The main PR updates the existing src/components/ActivityFeed.tsx fetch/polling flow (adding rate-limit-aware error handling and an error banner) that was introduced by the retrieved PR's initial Activity Feed implementation.

Suggested labels

type:feature, quality:clean, level:intermediate

Poem

🐇 A rabbit hops through API calls with glee,
Rate limits caught before they make us flee!
Headers read, errors shown so clear,
Dark text shines—no more confusion here! 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding GitHub API rate limit handling UI. It is concise, clear, and specific to the primary objective.
Description check ✅ Passed The PR description follows the template with all required sections filled: Related Issue, Description, How Has This Been Tested, Screenshots, and Type of Change. Details are comprehensive and specific.
Linked Issues check ✅ Passed All objectives from issue #496 are met: GitHub API 403 detection, user-friendly error messages, remaining request display via X-RateLimit-Remaining, reset time via X-RateLimit-Reset, and improved error UI for both themes.
Out of Scope Changes check ✅ Passed All changes are in-scope: ActivityFeed rate limit handling, error state management, dark mode text styling, and responsive error UI directly address issue #496 requirements. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Thank you @ananyadarna for your contribution. Please make sure your PR follows https://github.com/GitMetricsLab/github_tracker/blob/main/CONTRIBUTING.md#-pull-request-guidelines

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ActivityFeed.tsx`:
- Around line 51-55: The error message passed to setError in ActivityFeed.tsx
uses a multiline template literal which introduces unintended whitespace and
newlines; replace it with a single-line interpolated string (e.g. `GitHub API
rate limit exceeded. Please try again after ${resetTime}. Remaining Requests:
${remaining}`) or build the message via concatenation/trim to eliminate extra
spacing before calling setError; update the invocation where setError(...) is
used so the alert text is a compact single-line string.
- Around line 40-60: The code treats any 403 as a rate-limit and can let stale
responses overwrite state; update the fetchEvents logic in ActivityFeed to: 1)
detect rate-limit only when rate-limit headers indicate it (e.g., const
remaining = res.headers.get("X-RateLimit-Remaining"); if (remaining === "0") {
const reset = res.headers.get("X-RateLimit-Reset"); const resetTime = reset ?
new Date(Number(reset) * 1000).toLocaleTimeString() : "Unknown";
setError(`GitHub API rate limit exceeded. Please try again after ${resetTime}.
Remaining Requests: ${remaining}`); } else if (res.status === 403) {
setError("Access forbidden"); } 2) introduce an AbortController and/or a
monotonically increasing requestId inside useEffect around fetchEvents, pass
signal to fetch, abort on cleanup, and ignore responses whose requestId is stale
before calling setEvents/setError/setLoading; and 3) make the rate-limit message
a single-line string to avoid whitespace issues.
- Around line 30-77: fetchEvents can produce out-of-order state updates because
in-flight fetches aren't canceled; update fetchEvents to create an
AbortController per invocation (or a monotonic requestId) and pass its signal to
fetch, store the controller/requestId in the surrounding useEffect scope, and
cancel it in the useEffect cleanup (along with clearInterval). In fetchEvents,
detect aborted requests (e.g., err.name === 'AbortError' or
controller.signal.aborted / mismatched requestId) and return early so you do not
call setEvents, setError, or setLoading(false) for aborted/outdated requests;
ensure the finally block also skips setLoading when the request was aborted. Use
the function name fetchEvents and state setters setEvents, setError, setLoading
to locate the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa1d36fe-eb07-48d5-9bba-29c95cf65525

📥 Commits

Reviewing files that changed from the base of the PR and between 6c6bc3e and c877d31.

📒 Files selected for processing (2)
  • src/components/ActivityFeed.tsx
  • src/pages/Activity.tsx

Comment on lines 30 to +77
const fetchEvents = async () => {
try {
setLoading(true);

const res = await fetch(
`https://api.github.com/users/${username}/events`
);
const data = await res.json();

setEvents(data);
setLoading(false);
} catch (err) {
console.error(err);
setLoading(false);
}
try {
setLoading(true);
setError("");

const res = await fetch(
`https://api.github.com/users/${username}/events`
);

// Handle GitHub API rate limit
if (res.status === 403) {
const remaining =
res.headers.get("X-RateLimit-Remaining") || "0";

const reset =
res.headers.get("X-RateLimit-Reset");

const resetTime = reset
? new Date(Number(reset) * 1000).toLocaleTimeString()
: "Unknown";

setError(
`GitHub API rate limit exceeded.
Please try again after ${resetTime}.
Remaining Requests: ${remaining}`
);

setEvents([]);
setLoading(false);
return;
}

if (!res.ok) {
throw new Error("Failed to fetch activity");
}

const data = await res.json();

setEvents(data);
} catch (err) {
console.error(err);

setError(
"Something went wrong while fetching GitHub activity."
);
} finally {
setLoading(false);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ls -la
echo "---- ActivityFeed.tsx (lines 1-200) ----"
sed -n '1,200p' src/components/ActivityFeed.tsx
echo "---- ActivityFeed.tsx (lines 200-400) ----"
sed -n '200,400p' src/components/ActivityFeed.tsx

Repository: GitMetricsLab/github_tracker

Length of output: 5532


Guard against stale/out-of-order GitHub fetches in ActivityFeed.
fetchEvents runs on mount and every 30s, but the cleanup only clears the interval; in-flight fetch calls aren’t canceled. When username changes or the component unmounts, earlier responses can still call setEvents/setError/setLoading(false), overwriting newer state. Add an AbortController (or request-id guard) canceled in cleanup, and skip all state updates (including setLoading(false) in finally) for aborted/outdated requests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ActivityFeed.tsx` around lines 30 - 77, fetchEvents can
produce out-of-order state updates because in-flight fetches aren't canceled;
update fetchEvents to create an AbortController per invocation (or a monotonic
requestId) and pass its signal to fetch, store the controller/requestId in the
surrounding useEffect scope, and cancel it in the useEffect cleanup (along with
clearInterval). In fetchEvents, detect aborted requests (e.g., err.name ===
'AbortError' or controller.signal.aborted / mismatched requestId) and return
early so you do not call setEvents, setError, or setLoading(false) for
aborted/outdated requests; ensure the finally block also skips setLoading when
the request was aborted. Use the function name fetchEvents and state setters
setEvents, setError, setLoading to locate the changes.

Comment on lines +40 to +60
if (res.status === 403) {
const remaining =
res.headers.get("X-RateLimit-Remaining") || "0";

const reset =
res.headers.get("X-RateLimit-Reset");

const resetTime = reset
? new Date(Number(reset) * 1000).toLocaleTimeString()
: "Unknown";

setError(
`GitHub API rate limit exceeded.
Please try again after ${resetTime}.
Remaining Requests: ${remaining}`
);

setEvents([]);
setLoading(false);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant section with context
sed -n '1,220p' src/components/ActivityFeed.tsx | nl -ba | sed -n '1,220p' | cat

# Narrow search for fetch/useEffect and error handling
rg -n "useEffect|fetch\\(|res\\.status|403|X-RateLimit-Remaining|X-RateLimit-Reset|setError\\(" src/components/ActivityFeed.tsx

# Print a tighter window around the snippet area
sed -n '1,140p' src/components/ActivityFeed.tsx | nl -ba | sed -n '20,120p' | cat

Repository: GitMetricsLab/github_tracker

Length of output: 115


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Basic sanity: file exists?
ls -la src/components/ActivityFeed.tsx

# Show the file with line numbers (full file may be small; otherwise window it)
wc -l src/components/ActivityFeed.tsx

# Print the likely region
sed -n '1,220p' src/components/ActivityFeed.tsx | cat -n

# Focused grep for relevant patterns
rg -n "useEffect|fetch\\(|res\\.status|\\b403\\b|X-RateLimit-Remaining|X-RateLimit-Reset|setError\\(" src/components/ActivityFeed.tsx

# Print a tighter window around the 403 handling by searching for the snippet
line=$(rg -n "\\bres\\.status\\s*===\\s*403\\b" src/components/ActivityFeed.tsx | cut -d: -f1 | head -n1 || true)
if [ -n "${line}" ]; then
  start=$((line-30))
  end=$((line+60))
  if [ "$start" -lt 1 ]; then start=1; fi
  sed -n "${start},${end}p" src/components/ActivityFeed.tsx | cat -n
fi

# Check if there's any abort controller or cancellation logic
rg -n "AbortController|abort\\(|signal\\b|cleanup\\b|return \\(\\) =>|didCancel|isMounted|requestId" src/components/ActivityFeed.tsx

Repository: GitMetricsLab/github_tracker

Length of output: 8251


Fix incorrect 403 classification and stale fetch state in ActivityFeed

  • res.status === 403 is currently treated as a GitHub rate-limit error unconditionally (and X-RateLimit-Remaining defaults to "0" if missing), which will mislabel other 403 responses. Gate the rate-limit message on rate-limit headers instead (e.g., X-RateLimit-Remaining === "0"), otherwise show a generic forbidden/error.
Suggested fix
-          if (res.status === 403) {
-            const remaining =
-              res.headers.get("X-RateLimit-Remaining") || "0";
+          if (res.status === 403) {
+            const remaining = res.headers.get("X-RateLimit-Remaining");
             const reset =
               res.headers.get("X-RateLimit-Reset");
+            const isRateLimited = remaining === "0";
 
-            const resetTime = reset
-              ? new Date(Number(reset) * 1000).toLocaleTimeString()
-              : "Unknown";
-
-            setError(
-              `GitHub API rate limit exceeded.
-               Please try again after ${resetTime}.
-               Remaining Requests: ${remaining}`
-            );
+            if (isRateLimited) {
+              const resetTime = reset
+                ? new Date(Number(reset) * 1000).toLocaleTimeString()
+                : "Unknown";
+              setError(
+                `GitHub API rate limit exceeded. Please try again after ${resetTime}. Remaining Requests: ${remaining}`
+              );
+            } else {
+              setError("Access forbidden while fetching GitHub activity.");
+            }
 
             setEvents([]);
             setLoading(false);
             return;
           }
  • useEffect uses setInterval(fetchEvents, 30000) and triggers fetchEvents() again without any request cancellation/in-flight guard; overlapping or in-flight responses can overwrite newer state when username changes or when calls race. Add an AbortController/signal and/or a request-id guard before calling setEvents/setError/setLoading.

  • Minor: the rate-limit message is a multiline template literal; simplify it to a single-line string to avoid awkward whitespace in the rendered text.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (res.status === 403) {
const remaining =
res.headers.get("X-RateLimit-Remaining") || "0";
const reset =
res.headers.get("X-RateLimit-Reset");
const resetTime = reset
? new Date(Number(reset) * 1000).toLocaleTimeString()
: "Unknown";
setError(
`GitHub API rate limit exceeded.
Please try again after ${resetTime}.
Remaining Requests: ${remaining}`
);
setEvents([]);
setLoading(false);
return;
}
if (res.status === 403) {
const remaining = res.headers.get("X-RateLimit-Remaining");
const reset =
res.headers.get("X-RateLimit-Reset");
const isRateLimited = remaining === "0";
if (isRateLimited) {
const resetTime = reset
? new Date(Number(reset) * 1000).toLocaleTimeString()
: "Unknown";
setError(
`GitHub API rate limit exceeded. Please try again after ${resetTime}. Remaining Requests: ${remaining}`
);
} else {
setError("Access forbidden while fetching GitHub activity.");
}
setEvents([]);
setLoading(false);
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ActivityFeed.tsx` around lines 40 - 60, The code treats any
403 as a rate-limit and can let stale responses overwrite state; update the
fetchEvents logic in ActivityFeed to: 1) detect rate-limit only when rate-limit
headers indicate it (e.g., const remaining =
res.headers.get("X-RateLimit-Remaining"); if (remaining === "0") { const reset =
res.headers.get("X-RateLimit-Reset"); const resetTime = reset ? new
Date(Number(reset) * 1000).toLocaleTimeString() : "Unknown"; setError(`GitHub
API rate limit exceeded. Please try again after ${resetTime}. Remaining
Requests: ${remaining}`); } else if (res.status === 403) { setError("Access
forbidden"); } 2) introduce an AbortController and/or a monotonically increasing
requestId inside useEffect around fetchEvents, pass signal to fetch, abort on
cleanup, and ignore responses whose requestId is stale before calling
setEvents/setError/setLoading; and 3) make the rate-limit message a single-line
string to avoid whitespace issues.

Comment on lines +51 to +55
setError(
`GitHub API rate limit exceeded.
Please try again after ${resetTime}.
Remaining Requests: ${remaining}`
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Error string formatting includes unintended whitespace/newlines.

The multiline template literal will render with extra spacing. Use a single-line template string for cleaner alert text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ActivityFeed.tsx` around lines 51 - 55, The error message
passed to setError in ActivityFeed.tsx uses a multiline template literal which
introduces unintended whitespace and newlines; replace it with a single-line
interpolated string (e.g. `GitHub API rate limit exceeded. Please try again
after ${resetTime}. Remaining Requests: ${remaining}`) or build the message via
concatenation/trim to eliminate extra spacing before calling setError; update
the invocation where setError(...) is used so the alert text is a compact
single-line string.

@mehul-m-prajapati mehul-m-prajapati merged commit 764cf01 into GitMetricsLab:main May 29, 2026
4 of 6 checks passed
@github-actions
Copy link
Copy Markdown

🎉🎉 Thank you for your contribution! Your PR #533 has been merged! 🎉🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🚀 Feature: Add GitHub API Rate Limit Handling UI

2 participants